/*
	Trabalho 1 de Software Basico 
	Luiza de Noronha e Silva
	Igor Blumberg

	O que esta funcionando: o Makefile (os arquivos compilam)
	O que nao esta funcionando: o resto
*/

#include <stdio.h>
#include "compacta.h"

/* Prototipos das funcoes auxiliares usadas */

	/* tamanho da struct: funcao que, dado o vetor campos, calcula o tamanho 
	da struct com o padding e retorna ele */
	/* Essa funcao foi testada e nao apresentou falhas ate agora */

size_t tamanhoStruct( char campos[] );

	/* grava em little endian: recebe um vetor void* e grava ele num arquivo, 
	com formato little endian, alem d'um header com a especificacao do formato */
	/* Falta so adicionar o header, estava com sono e nao o fiz */

int gravaNormal( void* valores, int tamanho, char campos[], FILE* arquivo );

	/* grava em big endian: recebe um vetor void* e grava ele num arquivo, 
	com formato big endian, alem d'um header com a especificacao do formato */
	/* Idem aqui */

int gravaReverso( void* valores, int tamanho, char campos[], FILE* arquivo );

	/* testa sistema: determina se o sistema que o programa esta sendo rodado
	e little endian ou big endian */
	/* nao tenho nenhuma maquina big endian pra testar, mas funciona em little */

int testaSys();

/* Fim dos prototipos */


/* Implementacoes das funcoes  auxiliares usadas */
size_t tamanhoStruct( char campos[] ) {
	int i=0; /* iterador para campos */
	int bytesRest= sizeof(int); /* quantos bytes faltam ser preenchidos para completar a
			palavra do processador */
	size_t totalBytes=0; /* soma total de bytes */
	int tamEl; /* variavel auxiliar para reduzir o codigo */

	for (i=0; campos[i]!=0; i++) {
		switch (campos[i]) {
			case 1:
				tamEl=sizeof(char);
				break;
			case 2:
				tamEl=sizeof(short);
				break;
			case 3:
				tamEl=sizeof(int);
				break;
		} /* switch */

		if (bytesRest >= tamEl) { /* ainda da pra colocar coisa */
			bytesRest -= tamEl;
		} else { 
			totalBytes += sizeof(int);
			bytesRest = sizeof(int) - tamEl; 
		} /* if */
	} /* for */

	if (bytesRest) /* se o numero de bytes que tiverem restando for diferente de zero, a struct ocupara uma
			palavra adicional */
		totalBytes+= sizeof(int);

	return totalBytes;
}

int gravaNormal( void* valores, int tamanho, char campos[], FILE* arquivo ) {
	int i, j; /* i - iterador por campos; j - iterador pelo array  */
	int offset; size_t tamEl; /* offset e o deslocamento do ponteiro na struct, tamEl e o tamanho do elemento
			que esta sendo gravado */
	int tamStruct = tamanhoStruct(campos); /* tamaStruct e o tamanho total da struct COM PADDING */
	int bytesRest = sizeof(int);

	for ( j=0; j<tamanho; j++ ) {
		offset=0;

		for ( i=0; campos[i]!=0; i++ ) {		
			switch (campos[i]) {
				case 1:
					tamEl=sizeof(char);
					break;
				case 2:
					tamEl=sizeof(short);
					break;
				case 3:
					tamEl=sizeof(int);
					break;
			} /* switch */

			if (bytesRest < tamEl) { /* nesse caso, o padding tem que ser pulado */
				offset+=bytesRest;
				bytesRest = sizeof(int) - tamEl;
			} 

			fwrite( valores+(j*tamStruct)+offset, tamEl, 1, arquivo);

			offset+=tamEl;
		} /* for */
	} /* for */

	return 0; 	
}

int gravaReverso( void* valores, int tamanho, char campos[], FILE* arquivo ) {
	int i, j, k; /* i - iterador por campos; j - iterador pelo array; k - iterator pelos bytes do elemento  */
	int offset; size_t tamEl; /* offset e o deslocamento do ponteiro na struct, tamEl e o tamanho do elemento
			que esta sendo gravado */
	int tamStruct = tamanhoStruct(campos); /* tamaStruct e o tamanho total da struct COM PADDING */
	int bytesRest = sizeof(int);

	for ( j=0; j<tamanho; j++ ) {
		offset=0;

		for ( i=0; campos[i]!=0; i++ ) {		
			switch (campos[i]) {
				case 1:
					tamEl=sizeof(char);
					break;
				case 2:
					tamEl=sizeof(short);
					break;
				case 3:
					tamEl=sizeof(int);
					break;
			} /* switch */

			if (bytesRest < tamEl) { /* nesse caso, o padding tem que ser pulado */
				offset+=bytesRest;
				bytesRest = sizeof(int) - tamEl;
			} 

			for (k=offset+tamEl-1; k>= offset; k--)
				fwrite( valores+(j*tamStruct)+k, sizeof(char), 1, arquivo);

			offset+=tamEl;
		} /* for */
	} /* for */

	return 0;
}

int testaSys() {
	short one = 1; /* isso deve ser arquivado em memoria ou como 00 01 ou 01 00 */ 
   	char *byte = (char *) &one; /* como queremos olhar cada um desses bytes individualmente, podemos
					olhar esse short como um pequeno vetor de char */
   	return (byte[0] ? 1 : 0); /* Nessa abordagem, podemos finalmente testar se o primeiro
				byte e zero ou nao e concluir qual a arquitetura do sistema */ 
}

int gravacompactado( void* valores, int tamanho, char campos[], int little, FILE* arquivo ) {
	/* Antes de comecar a escrever qualquer coisa no arquivo, devemos determinar o tipo de
	arquitetura do sistema, little ou big endian. Se esperarmos sempre uma maquina little e o
	usuario rodar o programa numa maquina big, a gravacao sera feita ao contrario do esperado */

	if ( little == testaSys() ) { 
		gravaNormal( valores, tamanho, campos, arquivo );
	} else { 
		gravaReverso( valores, tamanho, campos, arquivo );
	} /* if */

	return 0;
}

void dump( FILE* arquivo ) {
	/* de novo */
}
